package demo10;
import java.util.*;

// 自己实现 BlockQueue
class MyBlockingQueue{
    public String[] elm =new String[10];
    // 由于下面三个变量在方法中需要多次修改，这里就要避免 内存可见性问题
    public volatile int size;
    public volatile int head;
    public volatile int tail;
    public synchronized void put(String str) throws InterruptedException {
        while(size == elm.length){
            // 满了之后, 阻塞等待别的线程取出元素,所以要进行加锁
            this.wait();
            // 这里当我们解除等待的时候，可能是由于notify 唤醒的wait ，也可能时 interrupt唤醒的，所以我们这里要来连续判断是否满了
        }
        size++;
        elm[tail]=str;
        tail++;
        if(tail == elm.length){
            // 环形
            tail=0;
        }
        // 这里解除take的锁，阻塞队列中存在元素了
        this.notify();
    }

    public synchronized String take() throws InterruptedException {
        while(size == 0){
            // 空 ，等待别的线程进行put元素
            this.wait();
        }
        String s=elm[head];
        size--;
        head++;
        if(head == elm.length){
            // 环形
            head=0;
        }
        // 当我们进行取出元素之后，就可以解除put的锁
        this.notify();
        return s;
    }
}

// 实现生产者消费者模型
public class Test24 {
    public static void main(String[] args) {
        MyBlockingQueue myBlockingQueue=new MyBlockingQueue();
        // 生产者
        Thread t1=new Thread(()->{
            int count=0;
            while(true){
                try {
                    myBlockingQueue.put(count+"");
                    System.out.println("生产元素："+(count++));
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });
        // 消费者
        Thread t2=new Thread(()->{
            while(true){
                try {
                    System.out.println("取元素："+myBlockingQueue.take());

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });


        t1.start();
        t2.start();
    }
}
